// <copyright file="IfInstruction.cs" company="Codeplex project Eco.Contrib"> 
// The MIT License (MIT) 
// </copyright> 
// <author>Peter Buchmann</author> 

namespace Eco.Contrib
{
    using System;
    using System.Collections.Generic;
    using Eco.ObjectRepresentation;
    using Eco.UmlCodeAttributes;

    /// <summary>
    /// IfInstruction: Instruction that represents a if statement
    /// </summary>
    public partial class IfInstruction
    {
        /// <summary>
        /// Executes the function.
        /// </summary>
        /// <param name="serviceProvider">The service provider.</param>
        /// <param name="variableList">The variable list.</param>
        /// <returns>Return value of executed function</returns>
        [UmlElement(Id = "311b9151-668a-4291-8511-583dd214bcc0")]
        protected internal override IElement ExecuteFunction(IEcoServiceProvider serviceProvider, IModifiableVariableList variableList)
        {
            bool condition;
            try
            {
                condition = this.Condition.ExecuteFunction(serviceProvider, variableList).GetValue<bool>();
            }
            catch (Exception ex)
            {
                throw new EcoContribException(string.Format("IF: Condition '{0}' doesn't evaluate to a boolean: ", this.Condition.OCL) + ex.ToString());
            }

            IElement element = null;
            if (condition)
            {
                element = this.TrueInstruction.Execute(serviceProvider, variableList);
            }
            else
            {
                if (this.FalseInstruction != null)
                {
                    element = this.FalseInstruction.Execute(serviceProvider, variableList);
                }
            }

            return element;
        }

        /// <summary>
        /// Inits the specified tokens.
        /// </summary>
        /// <param name="tokens">The tokens.</param>
        [UmlElement(Id = "38704e1d-d901-4064-a0ae-9d5a98f34551")]
        protected internal override void Init(Dictionary<IList<EslToken>, Type> tokens)
        {
            List<EslToken> tokenList = new List<EslToken>();
            tokenList.Add(new EslToken(EslTokenType.Word, "If"));
            tokenList.Add(new EslToken(EslTokenType.Word, "Ocl"));
            tokenList.Add(new EslToken(EslTokenType.Operator, "("));
            tokens.Add(tokenList, this.GetType());
            tokenList = new List<EslToken>();
            tokenList.Add(new EslToken(EslTokenType.Word, "If"));
            tokenList.Add(new EslToken(EslTokenType.Word, "Eal"));
            tokenList.Add(new EslToken(EslTokenType.Operator, "("));
            tokens.Add(tokenList, this.GetType());
            tokenList = new List<EslToken>();
            tokenList.Add(new EslToken(EslTokenType.Word, "If"));
            tokenList.Add(new EslToken(EslTokenType.Word, "OclPs"));
            tokenList.Add(new EslToken(EslTokenType.Operator, "("));
            tokens.Add(tokenList, this.GetType());
        }

        /// <summary>
        /// Parses the specified token list.
        /// </summary>
        /// <param name="tokenList">The token list.</param>
        /// <param name="ecoScript">The eco script.</param>
        /// <param name="fullExpression">if set to <c>true</c> [full expression].</param>
        [UmlElement(Id = "4501842a-15b1-4bbd-ac67-007b709ec3e9")]
        protected internal override void Parse(IList<EslToken> tokenList, EcoScript ecoScript, bool fullExpression)
        {
            EslToken token = GetNextToken(tokenList);
            RememberEcoScript(ecoScript, token);

            EslInstruction instruction = ecoScript.ReadNextInstruction(tokenList, false);

            if (!(instruction is InstructionWithOcl))
            {
                throw new EslException("Instruction with OCL expected at " + token.ToString(), token);
            }

            this.Condition = instruction as InstructionWithOcl;

            token = GetNextToken(tokenList);
            if ((token.TokenType != EslTokenType.Word) || (token.TokenValue.ToLower() != "then"))
            {
                throw new EslException("keyword 'then' expected at " + token.ToString(), token);
            }

            // Read true block
            bool finished = false;
            EslInstruction lastInstruction = null;
            do
            {
                instruction = ecoScript.ReadNextInstruction(tokenList, true);
                if (instruction != null)
                {
                    if (lastInstruction == null)
                    {
                        this.TrueInstruction = instruction;
                    }
                    else
                    {
                        lastInstruction.NextInstruction = instruction;
                    }

                    lastInstruction = instruction;
                }

                finished = (instruction == null) || (tokenList.Count == 0) ||
                    ((tokenList[0].TokenType == EslTokenType.Word) &&
                    ((tokenList[0].TokenValue.ToLower() == "end") || (tokenList[0].TokenValue.ToLower() == "else")));
            }
            while (!finished);

            token = GetNextToken(tokenList);
            if ((token.TokenType == EslTokenType.Word) && (token.TokenValue.ToLower() == "else"))
            {
                finished = false;
                lastInstruction = null;
                do
                {
                    instruction = ecoScript.ReadNextInstruction(tokenList, true);
                    if (instruction != null)
                    {
                        if (lastInstruction == null)
                        {
                            this.FalseInstruction = instruction;
                        }
                        else
                        {
                            lastInstruction.NextInstruction = instruction;
                        }

                        lastInstruction = instruction;
                    }

                    finished = instruction == null || tokenList.Count == 0 ||
                        (tokenList[0].TokenType == EslTokenType.Word && tokenList[0].TokenValue.ToLower() == "end");
                }
                while (!finished);
                token = GetNextToken(tokenList);
            }

            if ((token.TokenType != EslTokenType.Word) || (token.TokenValue.ToLower() != "end"))
            {
                throw new EslException("keyword 'end' expected at token " + token.ToString(), token);
            }

            if (fullExpression)
            {
                EslToken tokenEnd = GetNextToken(tokenList);
                AssertInstructionEnd(tokenEnd);
            }
        }
    }
}